﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Ckknight.StateMachine
{
    public sealed partial class StateMachine<TState, TTrigger, TModel>
    {
        /// <summary>
        /// Represents a currently-occurring transition between two states (possibly equivalent) through a specific trigger.
        /// </summary>
        public class Transition
        {
            #region Private fields

            private readonly StateMachine<TState, TTrigger, TModel> _machine;
            private readonly TTrigger _trigger;
            private readonly TState _previousState;
            private bool _hasNewState;
            private TState _newState;

            #endregion

            #region Internal constructor

            /// <summary>
            /// Instantiate a new Transition
            /// </summary>
            /// <param name="machine">The StateMachine this transition is occurring on</param>
            /// <param name="trigger">The trigger which caused the transition to occur</param>
            /// <param name="previousState">The state of the machine before the transition</param>
            internal Transition(StateMachine<TState, TTrigger, TModel> machine, TTrigger trigger, TState previousState)
            {
                _machine = machine;
                _trigger = trigger;
                _previousState = previousState;
                _hasNewState = false;
                _newState = default(TState);
            }

            /// <summary>
            /// Instantiate a new Transition
            /// </summary>
            /// <param name="machine">The StateMachine this transition is occurring on</param>
            /// <param name="trigger">The trigger which caused the transition to occur</param>
            /// <param name="previousState">The state of the machine before the transition</param>
            /// <param name="newState">The state of the machine after the transition</param>
            internal Transition(StateMachine<TState, TTrigger, TModel> machine, TTrigger trigger, TState previousState, TState newState)
            {
                _machine = machine;
                _trigger = trigger;
                _previousState = previousState;
                _hasNewState = true;
                _newState = newState;
            }

            #endregion

            #region Public properties

            /// <summary>
            /// The StateMachine this transition is occurring on
            /// </summary>
            public StateMachine<TState, TTrigger, TModel> Machine
            {
                get
                {
                    return _machine;
                }
            }

            /// <summary>
            /// The model of the StateMachine
            /// </summary>
            public TModel Model
            {
                get
                {
                    return _machine.Model;
                }
            }

            /// <summary>
            /// The trigger which caused the transition to occur
            /// </summary>
            public TTrigger Trigger
            {
                get
                {
                    return _trigger;
                }
            }

            /// <summary>
            /// The state of the machine before the transition
            /// </summary>
            public TState PreviousState
            {
                get
                {
                    return _previousState;
                }
            }

            /// <summary>
            /// Whether the transition knows which state it is transitioning to.
            /// </summary>
            public bool HasNewState
            {
                get
                {
                    return _hasNewState;
                }
            }

            /// <summary>
            /// The state of the machine after the transition.
            /// 
            /// This might not be valid if the transition has failed or the new state has not been calculated yet.
            /// </summary>
            /// <exception cref="System.InvalidOperationException">
            /// Thrown when the NewState is not properly calculated.
            /// </exception>
            public TState NewState
            {
                get
                {
                    if (!_hasNewState)
                    {
                        throw new InvalidOperationException("NewState has not been calculated");
                    }
                    return _newState;
                }
                internal set
                {
                    if (_hasNewState)
                    {
                        throw new InvalidOperationException("NewState has not already been calculated");
                    }
                    _newState = value;
                }
            }

            #endregion
        }

        /// <summary>
        /// Represents a currently-occurring transition between two states (possibly equivalent) through a specific trigger.
        /// 
        /// Also includes a parameter passed along with the trigger.
        /// </summary>
        public class Transition<TArg1> : Transition
        {
            #region Private fields

            private readonly TArg1 _item1;

            #endregion

            #region Internal Constructor

            /// <summary>
            /// Instantiate a new Transition
            /// </summary>
            /// <param name="machine">The StateMachine this transition is occurring on</param>
            /// <param name="trigger">The trigger which caused the transition to occur</param>
            /// <param name="previousState">The state of the machine before the transition</param>
            /// <param name="item1">The first trigger parameter</param>
            internal Transition(StateMachine<TState, TTrigger, TModel> machine, TTrigger trigger, TState previousState, TArg1 item1)
                : base(machine, trigger, previousState)
            {
                _item1 = item1;
            }

            /// <summary>
            /// Instantiate a new Transition
            /// </summary>
            /// <param name="machine">The StateMachine this transition is occurring on</param>
            /// <param name="trigger">The trigger which caused the transition to occur</param>
            /// <param name="previousState">The state of the machine before the transition</param>
            /// <param name="newState">The state of the machine after the transition</param>
            /// <param name="item1">The first trigger parameter</param>
            internal Transition(StateMachine<TState, TTrigger, TModel> machine, TTrigger trigger, TState previousState, TState newState, TArg1 item1)
                : base(machine, trigger, previousState, newState)
            {
                _item1 = item1;
            }

            #endregion

            /// <summary>
            /// The first trigger parameter
            /// </summary>
            public TArg1 Item1
            {
                get
                {
                    return _item1;
                }
            }
        }

        /// <summary>
        /// Represents a currently-occurring transition between two states (possibly equivalent) through a specific trigger.
        /// 
        /// Also includes two parameters passed along with the trigger.
        /// </summary>
        public class Transition<TArg1, TArg2> : Transition<TArg1>
        {
            #region Private fields

            private readonly TArg2 _item2;

            #endregion

            #region Internal Constructor

            /// <summary>
            /// Instantiate a new Transition
            /// </summary>
            /// <param name="machine">The StateMachine this transition is occurring on</param>
            /// <param name="trigger">The trigger which caused the transition to occur</param>
            /// <param name="previousState">The state of the machine before the transition</param>
            /// <param name="item1">The first trigger parameter</param>
            /// <param name="item2">The second trigger parameter</param>
            internal Transition(StateMachine<TState, TTrigger, TModel> machine, TTrigger trigger, TState previousState, TArg1 item1, TArg2 item2)
                : base(machine, trigger, previousState, item1)
            {
                _item2 = item2;
            }

            /// <summary>
            /// Instantiate a new Transition
            /// </summary>
            /// <param name="machine">The StateMachine this transition is occurring on</param>
            /// <param name="trigger">The trigger which caused the transition to occur</param>
            /// <param name="previousState">The state of the machine before the transition</param>
            /// <param name="newState">The state of the machine after the transition</param>
            /// <param name="item1">The first trigger parameter</param>
            /// <param name="item2">The second trigger parameter</param>
            internal Transition(StateMachine<TState, TTrigger, TModel> machine, TTrigger trigger, TState previousState, TState newState, TArg1 item1, TArg2 item2)
                : base(machine, trigger, previousState, newState, item1)
            {
                _item2 = item2;
            }

            #endregion

            /// <summary>
            /// The second trigger parameter
            /// </summary>
            public TArg2 Item2
            {
                get
                {
                    return _item2;
                }
            }
        }

        /// <summary>
        /// Represents a currently-occurring transition between two states (possibly equivalent) through a specific trigger.
        /// 
        /// Also includes three parameters passed along with the trigger.
        /// </summary>
        public class Transition<TArg1, TArg2, TArg3> : Transition<TArg1, TArg2>
        {
            #region Private fields

            private readonly TArg3 _item3;

            #endregion

            #region Internal Constructor

            /// <summary>
            /// Instantiate a new Transition
            /// </summary>
            /// <param name="machine">The StateMachine this transition is occurring on</param>
            /// <param name="trigger">The trigger which caused the transition to occur</param>
            /// <param name="previousState">The state of the machine before the transition</param>
            /// <param name="item1">The first trigger parameter</param>
            /// <param name="item2">The second trigger parameter</param>
            /// <param name="item3">The third trigger parameter</param>
            internal Transition(StateMachine<TState, TTrigger, TModel> machine, TTrigger trigger, TState previousState, TArg1 item1, TArg2 item2, TArg3 item3)
                : base(machine, trigger, previousState, item1, item2)
            {
                _item3 = item3;
            }

            /// <summary>
            /// Instantiate a new Transition
            /// </summary>
            /// <param name="machine">The StateMachine this transition is occurring on</param>
            /// <param name="trigger">The trigger which caused the transition to occur</param>
            /// <param name="previousState">The state of the machine before the transition</param>
            /// <param name="newState">The state of the machine after the transition</param>
            /// <param name="item1">The first trigger parameter</param>
            /// <param name="item2">The second trigger parameter</param>
            /// <param name="item3">The third trigger parameter</param>
            internal Transition(StateMachine<TState, TTrigger, TModel> machine, TTrigger trigger, TState previousState, TState newState, TArg1 item1, TArg2 item2, TArg3 item3)
                : base(machine, trigger, previousState, newState, item1, item2)
            {
                _item3 = item3;
            }

            #endregion

            /// <summary>
            /// The third trigger parameter
            /// </summary>
            public TArg3 Item3
            {
                get
                {
                    return _item3;
                }
            }
        }

        /// <summary>
        /// Represents a currently-occurring transition between two states (possibly equivalent) through a specific trigger.
        /// 
        /// Also includes four parameters passed along with the trigger.
        /// </summary>
        public sealed class Transition<TArg1, TArg2, TArg3, TArg4> : Transition<TArg1, TArg2, TArg3>
        {
            #region Private fields

            private readonly TArg4 _item4;

            #endregion

            #region Internal Constructor

            /// <summary>
            /// Instantiate a new Transition
            /// </summary>
            /// <param name="machine">The StateMachine this transition is occurring on</param>
            /// <param name="trigger">The trigger which caused the transition to occur</param>
            /// <param name="previousState">The state of the machine before the transition</param>
            /// <param name="item1">The first trigger parameter</param>
            /// <param name="item2">The second trigger parameter</param>
            /// <param name="item3">The third trigger parameter</param>
            /// <param name="item4">The fourth trigger parameter</param>
            internal Transition(StateMachine<TState, TTrigger, TModel> machine, TTrigger trigger, TState previousState, TArg1 item1, TArg2 item2, TArg3 item3, TArg4 item4)
                : base(machine, trigger, previousState, item1, item2, item3)
            {
                _item4 = item4;
            }

            /// <summary>
            /// Instantiate a new Transition
            /// </summary>
            /// <param name="machine">The StateMachine this transition is occurring on</param>
            /// <param name="trigger">The trigger which caused the transition to occur</param>
            /// <param name="previousState">The state of the machine before the transition</param>
            /// <param name="newState">The state of the machine after the transition</param>
            /// <param name="item1">The first trigger parameter</param>
            /// <param name="item2">The second trigger parameter</param>
            /// <param name="item3">The third trigger parameter</param>
            /// <param name="item4">The fourth trigger parameter</param>
            internal Transition(StateMachine<TState, TTrigger, TModel> machine, TTrigger trigger, TState previousState, TState newState, TArg1 item1, TArg2 item2, TArg3 item3, TArg4 item4)
                : base(machine, trigger, previousState, newState, item1, item2, item3)
            {
                _item4 = item4;
            }

            #endregion

            /// <summary>
            /// The fourth trigger parameter
            /// </summary>
            public TArg4 Item4
            {
                get
                {
                    return _item4;
                }
            }
        }
    }
}
